#ifndef _AXISALIGNEDBOX_H_
#define _AXISALIGNEDBOX_H_

#include "Vector.h"
#include <algorithm>

namespace QuadTreeLib
{

inline float PlaneDotCoord(const Plane& pP, const Vector3& pV)
{
	return (pP.a*pV.x + pP.b*pV.y + pP.c*pV.z + pP.d);
}

struct AABB
{
	enum IntersectType
	{
	   IT_Outside,
	   IT_Intersect,
	   IT_Inside
	};

	Vector3 Min;
	Vector3 Max;

	IntersectType TestInBoundingPlanes( const Plane planes[] )
	{
		Vector3 corners[9];

		corners[0].x = Min.x;
		corners[0].y = Min.y;
		corners[0].z = Min.z;

		corners[1].x = Min.x;
		corners[1].y = Max.y;
		corners[1].z = Min.z;

		corners[2].x = Max.x;
		corners[2].y = Min.y;
		corners[2].z = Min.z;

		corners[3].x = Max.x;
		corners[3].y = Max.y;
		corners[3].z = Min.z;

		corners[4].x = Min.x;
		corners[4].y = Min.y;
		corners[4].z = Max.z;

		corners[5].x = Min.x;
		corners[5].y = Max.y;
		corners[5].z = Max.z;

		corners[6].x = Max.x;
		corners[6].y = Min.y;
		corners[6].z = Max.z;

		corners[7].x = Max.x;
		corners[7].y = Max.y;
		corners[7].z = Max.z;
		
		corners[8] = (Min + Max) * 0.5f;

		Vector3 boxSize = Max - Min;
		float size = boxSize.length();

		// test box's bounding sphere against all planes - removes many false tests, adds one more check
		for(int p = 0; p < 6; p++) 
		{
			float centDist = PlaneDotCoord( planes[p], corners[8] );
			if( centDist < -size/2 )
				return IT_Outside;
		}

		int totalIn = 0;
		size /= 6.0f; //reduce size to 1/4 (half of radius) for more precision!! // tweaked to 1/6, more sensible

		// test all 8 corners and 9th center point against the planes
		// if all points are behind 1 specific plane, we are out
		// if we are in with all points, then we are fully in
		for(int p = 0; p < 6; p++) 
		{
			int inCount = 9;
			int ptIn = 1;

			for(int i = 0; i < 9; ++i) 
			{

				// test this point against the planes
				float distance = PlaneDotCoord( planes[p], corners[i] );
				if (distance < -size) 
				{
					ptIn = 0;
					inCount--;
				}
			}

			// were all the points outside of plane p?
			if (inCount == 0) 
			{
				//assert( completelyIn == false );
				return IT_Outside;
			}

			// check if they were all on the right side of the plane
			totalIn += ptIn;
		}

		if( totalIn == 6 )
			return IT_Inside;

		return IT_Intersect;
	}

	
	float MinDistanceFromPointSq( const Vector3 & point )
	{
		float dist = 0.0f;

		if (point.x < Min.x)
		{
			float d=point.x-Min.x;
			dist+=d*d;
		}
		else if (point.x > Max.x)
		{
			float d=point.x-Max.x;
			dist+=d*d;
		}

		if (point.y < Min.y)
		{
			float d=point.y-Min.y;
			dist+=d*d;
		}
		else if (point.y > Max.y)
		{
			float d=point.y-Max.y;
			dist+=d*d;
		}

		if (point.z < Min.z)
		{
			float d=point.z-Min.z;
			dist+=d*d;
		}
		else if (point.z > Max.z)
		{
			float d=point.z-Max.z;
			dist+=d*d;
		}

		return dist;
	}

	float MaxDistanceFromPointSq( const Vector3 & point )
	{
		float dist = 0.0f;
		float k;

		k = std::max( fabsf( point.x - Min.x ), fabsf( point.x - Max.x ) );
		dist += k * k;

		k = std::max( fabsf( point.y - Min.y ), fabsf( point.y - Max.y ) );
		dist += k * k;

		k = std::max( fabsf( point.z - Min.z ), fabsf( point.z - Max.z ) );
		dist += k * k;

		return dist;
	}

	bool IntersectSphereSq( const Vector3 & center, float radiusSq )
	{
		return MinDistanceFromPointSq( center ) <= radiusSq;
	}

	bool IsInsideSphereSq( const Vector3 & center, float radiusSq )
	{
		return MaxDistanceFromPointSq( center ) <= radiusSq;
	}
};

} // namespace QuadTreeLib
#endif
